package main

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"io"
	"k8s.io/klog/v2"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
)

type hp struct {
	ip     string
	port   int
	server http.Server
}

func (h *hp) String() string {
	return fmt.Sprintf("%s:%d", h.ip, h.port)
}

func (h *hp) Logger(f http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		rl := RequestLog{
			ip:        r.RemoteAddr,
			method:    r.Method,
			path:      r.RequestURI,
			query:     r.URL.RawQuery,
			startTime: time.Now().Unix(),
		}
		buf := new(bytes.Buffer)
		if _, err := buf.ReadFrom(r.Body); err != nil {
			w.WriteHeader(500)
			io.WriteString(w, "internal error")
			return
		}

		rl.requestBody = buf.String()

		wc := &ResponseWithCode{
			ResponseWriter: w,
			statusCode:     http.StatusOK,
			body:           bytes.Buffer{},
		}

		f.ServeHTTP(wc, r)
		rl.endTime = time.Now().Unix()
		rl.status = wc.statusCode
		rl.responseBody = wc.body.String()
		klog.Info(rl)
	})
}

func (h *hp) Run() {

	http.Handle("/hello", h.Logger(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		for key, value := range request.Header {
			writer.Header()[key] = value
		}
		time.Sleep(2 * time.Second)
		writer.Header().Set("VERSION", os.Getenv("VERSION"))
		io.WriteString(writer, "hello world")
	})))

	http.Handle("/healthz", h.Logger(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		time.Sleep(1 * time.Second)
		io.WriteString(writer, "ok")
	})))
	h.server = http.Server{Addr: h.String(), Handler: nil}
	klog.V(2).Infof("start listen: %s", h)

	if err := h.server.ListenAndServe(); http.ErrServerClosed != err {
		log.Fatalf("server not gracefully shutdown, err :%v\n", err)
	}

}

func (h *hp) Shutdown(ctx context.Context) error {
	return h.server.Shutdown(ctx)
}

type ResponseWithCode struct {
	http.ResponseWriter
	statusCode int
	body       bytes.Buffer
}

type RequestLog struct {
	ip           string
	method       string
	path         string
	query        string
	requestBody  string
	status       int
	responseBody string
	startTime    int64
	endTime      int64
}

func (l RequestLog) String() string {
	return fmt.Sprintf("%s | %s | %s | %s | %s | %d | %s | sTime %d | eTime %d ", l.ip, l.method,
		l.path, l.query, l.requestBody, l.status, l.responseBody, l.startTime, l.endTime)
}

func (rec *ResponseWithCode) WriteHeader(statusCode int) {
	rec.ResponseWriter.WriteHeader(statusCode)
	rec.statusCode = statusCode
}

func (rec *ResponseWithCode) Write(d []byte) (n int, err error) {
	n, err = rec.ResponseWriter.Write(d)
	if err != nil {
		return
	}
	rec.body.Write(d)
	return
}

func main() {
	klog.InitFlags(nil)
	flag.Parse()
	h := &hp{ip: "0.0.0.0", port: 8000}
	// make sure idle connections returned
	processed := make(chan struct{})
	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		<-c

		ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
		defer cancel()
		if err := h.Shutdown(ctx); nil != err {
			klog.Fatalf("server shutdown failed, err: %v\n", err)
		}
		klog.V(2).Info("server gracefully shutdown")

		close(processed)
	}()

	h.Run()
	<-processed
}
